﻿/*
 * KRPCOperation
 * 
 * Represents a KRPC operation
 * 
 * Author: snowdreamist
 * Date: 2011/12/7
 * 
 */

using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Text;
using System.Reflection;
using BitTorrentSharp.KRPC.Messages;
using BitTorrentSharp.KRPC.Contract;
using BitTorrentSharp.KRPC.Handler;
using BitTorrentSharp.KRPC.Threading;

namespace BitTorrentSharp.KRPC
{
    public enum ReturnMode
    {
        /// <summary>
        /// No return value
        /// </summary>
        None,
        /// <summary>
        /// No out parameter is allowed in this mode
        /// </summary>
        ReturnValue,
        /// <summary>
        /// Return type must be void in this mode
        /// </summary>
        OutParameter
    }

    public class KRPCOperation
    {
        internal const string ReturnValueKey = "ret";

        internal KRPCOperation(KRPCService service, MethodInfo method, KRPCOperationContractAttribute optAttr)
        {
            if (service == null)
                throw new ArgumentNullException("service");
            if (method == null)
                throw new ArgumentNullException("method");
            if (optAttr == null)
                throw new ArgumentNullException("optAttr");
            // check
            var paras = method.GetParameters();
            if (paras.Where(p => p.IsOut).Count() != 0 && method.ReturnType != typeof(void))
                throw new KRPCActiveException("Invalid KRPCOperation. Out parameters and return value cannot exist at same time");
            // service
            this.Service = service;
            // wrapper
            this.ContractWrapper = new OperationContractWrapper(method, optAttr);
            // parameters
            this.Parameters = new ReadOnlyCollection<KRPCParameterInfo>(paras.Select(p => new KRPCParameterInfo(p)).ToArray());
            // return type
            if (method.ReturnType != typeof(void))
                this.ReturnType = KRPCDataInfo.GetDataInfo(method.ReturnType);
        }
        public KRPCService Service { get; private set; }
        /// <summary>
        /// Wrapper
        /// </summary>
        public OperationContractWrapper ContractWrapper { get; private set; }
        /// <summary>
        /// Return mode
        /// </summary>
        public ReturnMode ReturnMode { get; private set; }
        /// <summary>
        /// Parameter names, used to get parameter in order
        /// </summary>
        public ReadOnlyCollection<KRPCParameterInfo> Parameters { get; private set; }
        /// <summary>
        /// The real return value type of this operation
        /// </summary>
        public KRPCDataInfo ReturnType { get; private set; }
        /// <summary>
        /// Invoke this operation
        /// ATTENTION:
        ///     The MessageContext.Parameters only contains the parameter deserialized from network, the type my not fit the method exactly.
        ///     eg. ref parameter
        ///     This method need to create ref parameter, assign it, create out parameter, assign to 
        /// </summary>
        /// <param name="context">message context</param>
        /// <returns>result</returns>
        public object Invoke(MessageContext context)
        {
            if (context == null)
                throw new ArgumentNullException("context");
            if (context.Operation == null)
                throw new ArgumentException("Operation cannot be null");

            throw new NotImplementedException();
        }
        /// <summary>
        /// Resume this a process of this method
        /// </summary>
        /// <param name="context">message context</param>
        /// <param name="invokeContext">invoke context</param>
        public void Resume(MessageContext context, KRPCAsyncInvokeContext invokeContext)
        {
            if (invokeContext.Operation != this)
                throw new KRPCInvokeException("Operation mismatch");
            // resume this method
            invokeContext.AsyncResult.WaitEvent.Set();
        }

        public override bool Equals(object obj)
        {
            if (obj is KRPCOperation)
                return this.ContractWrapper.Method == (obj as KRPCOperation).ContractWrapper.Method;
            return false;
        }

        public override int GetHashCode()
        {
            return this.ContractWrapper.Method.GetHashCode();
        }
    }
}
